/**
 * 写之前，先了解，promise提供了多少静态方法、原型方法，以及各个方法的作用
 * 静态方法 有：all、allSettled、any、race、reject、resolve
 * 原型方法 有：constructor、then、catch、finally、
 * 
 * 1.根据上述的描述，先实现一个空的壳,并将对应的 功能描述备注 接下来就可以一步步实现我们自己的Promise类了
 * 
 * 2.参考Promise的介绍实现 constructor ，实例化时传入 executor 函数，再由 executor 函数的描述 可以 推断出 我们接下来要做的事情
 * 2.1. 定义一个状态，用于保存当前实例的状态
 * 2.2. 实现一个resolve方法 即一个 rejected 方法
 * 
 * 3.定义一个保存成功回调的数组，一个保存失败回调的数组，用于 resolve， reject执行后的回调，使用数组的原因（方便后续 all、allSettled、any、race 等方法拓展）
 * 4.定义一个变量保存成功后的值，定义一个变量保存失败的原因
 * 5.实现一个then方法
 * 6.实现resolve和reject的静态方法
 */

const PENDING = 'pending'
const FULFILLEF = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    status = PENDING
    value = undefined
    reason = undefined
    successCallback = []
    failCallback = []
    /**
     * 此处可参考 Mozilla 上 对应的 Promise
     * resolve 和 reject 两个函数作为参数传递给executor（executor 函数在Promise构造函数返回所建promise实例对象前被调用）。resolve 和 reject 函数被调用时，分别将promise的状态改为fulfilled（完成）或rejected（失败）
     * @param {*} executor 
     */
    constructor (executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    
    /**
     * 该方法会等到传入参数都执行成功式返回成功，只要有一个失败立即返回失败，
     * 触发成功状态后将 array 按照传入的顺序 返回成功回调的值
     * 失败状态，则将第一个失败的错误信息放回
     * @param {*} array 为一个可迭代的对象
     */
    static all (array) {
        let results = []
        // 异步记数器
        let index = 0
        return new MyPromise((resolve, reject) => {
            function addData(key, value) {
                results[key] = value
                index++
                if (index === array.length) {
                    resolve(results)
                }
            }
            for (let i = 0; i < array.length; i++) {
                // 避免重复实例化
                let current = array[i]
                if (current instanceof MyPromise) {
                    current.then(value => addData(i, value), reason => reject(reason))
                } else {
                    addData(i, current)
                }
            }
        })
    }

    /**
     * 等待所有promise都完成（每个promise返回成功或者失败）返回一个promise的对象数组，对应每个promise的值
     * @param {*} array 
     */
    static allSettled (array) {
        let results = []
        // 异步记数器
        let index = 0
        return new MyPromise((resolve, reject) => {
            function addData(key, value, status) {
                results[key] = value
                index++
                if (index === array.length) {
                    resolve(results)
                }
            }
            for (let i = 0; i < array.length; i++) {
                // 避免重复实例化
                let current = array[i]
                if (current instanceof MyPromise) {
                    current.then(value => {
                        let item = {status: FULFILLEF, value: value}
                        addData(i, item)
                    }, reason => {
                        let item = {status: REJECTED, reason: reason}
                        addData(i, item)
                    })
                } else {
                    let item = {status: FULFILLEF, value: current}
                    addData(i, item)
                }
            }
        })
    }

    /**
     * 接受一个promise对象的集合，当其中一个promise成功，就返回那个成功的promise对象的值 目前处于实验性阶段
     * @param {*} map 
     */
    static any (map) {
        return new MyPromise((resolve, reject) => {
            for (let i = 0; i < map.length; i++) {
                // 避免重复实例化
                let current = map[i]
                if (current instanceof MyPromise) {
                    current.then(value => resolve(value))
                } else {
                    resolve(current)
                }
            }
        })
    }

    /**
     * 当参数中任意一个子promise被成功或者失败后，父promise马上会用子promise的成功返回值或者失败详情作为参数调用父promise绑定的句柄，并返回promise对象
     * @param {*} array 
     */
    static race (array) {
        return new MyPromise((resolve, reject) => {
            for (let i = 0; i < array.length; i++) {
                // 避免重复实例化
                let current = array[i]
                if (current instanceof MyPromise) {
                    current.then(value => resolve(value), reason => reject(reason))
                } else {
                    resolve(current)
                }
            }
        })
    }

    /**
     * 返回一个状态为失败的Promise对象，并将给定的失败信息传递给对应的方法
     * @param {*} reason 
     */
    static reject (reason) {
        if (reason instanceof MyPromise) return this.reject('Unhandled promise rejection')
        return new MyPromise((undefined, reject) => reject(reason))
    }

    /**
     * 返回一个状态由给定value决定的Promise对象，如果该值是thenable（即带有then方法的对象），返回的Promise对象的最终状态由then方法执行决定；否则的话（该value为空，基本类型或者不带then方法的对象），返回的Promise对象状态为 fulfilled，并且将该value传递给相应的then方法。
     * @param {*} value 
     */
    static resolve (value) {
        if (value instanceof MyPromise) return value
        return new MyPromise(resolve => resolve(value))
    }

    /**
     * 返回一个状态为失败的Promise对象，并将给定的失败信息传递给对应的方法
     * @param {*} reason 
     */
    reject = reason => {
        if (this.status !== PENDING) return
        this.status = REJECTED
        this.reason = reason
        while(this.failCallback.length) this.failCallback.shift()()
    }

    /**
     * 返回一个状态由给定value决定的Promise对象，如果该值是thenable（即带有then方法的对象），返回的Promise对象的最终状态由then方法执行决定；否则的话（该value为空，基本类型或者不带then方法的对象），返回的Promise对象状态为 fulfilled，并且将该value传递给相应的then方法。
     * @param {*} value 
     */
    resolve = value => {
        if (this.status !== PENDING) return
        this.status = FULFILLEF
        this.value = value
        while(this.successCallback.length) this.successCallback.shift()()
    }
    
    /**
     * 添加解决（fulfilled）和拒绝（rejected）回调到当前promise，返回一个新的promise，将以回调的返回值来 resolve
     * @param {*} successCallback 
     * @param {*} failCallback 
     */
    then(successCallback, failCallback) {
        // 根据描述可以知当前回返值为 promise 对象 且 successCallback为空时 返回上一个promise的值， failCallback 为空时 抛出当前的 错误信息
        successCallback = successCallback ? successCallback : value => value
        failCallback = failCallback ? failCallback : reason => {throw reason}
        // 初步实现
        let promise2 = new MyPromise((resolve, reject) => {
            // 根据当前状态 判断执行的方法
            if (this.status === FULFILLEF) {
                // 处理异步任务
                setTimeout(() => {
                    try {
                        let x = successCallback(this.value)
                        // console.log(x)
                        // 修复链式调用 问题
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        this.reject(e)
                    }
                }, 0)
            }  else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = failCallback(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        this.reject(e)
                    }
                }, 0)
            } else {
                try {
                    this.successCallback.push(() => {
                        setTimeout(() => {
                            try {
                                let x = successCallback(this.value)
                                // console.log(x)
                                // 修复链式调用 问题
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (e) {
                                this.reject(e)
                            }
                        }, 0)
                    })
                    this.failCallback.push(() => {
                        setTimeout(() => {
                            try {
                                let x = failCallback(this.reason)
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (e) {
                                this.reject(e)
                            }
                        }, 0)
                    })
                } catch (e) {
                    this.reject(e)
                }
            }
        })
        return promise2
    }

    /**
     * 添加一个拒绝回调到当前的promise，返回一个新的promise，当这个回调函数被调用，新的promise将以它的返回值来resolve，否则如果当前promise进入fulfilled状态，则以当前promise的完成结果作为新promise的完成成果
     * @param {*} failCallback 
     */
    catch (failCallback) {
        return this.then(undefined, failCallback)
    }

    /**
     * 添加一个事件处理回调于当前的promise对象，并且在原promise对象解析完毕后，返回一个新的promise对象，回调会在当前promise运行完毕后被调用，无论当前的promise状态是完成还是失败
     * @param {*} callback 
     */
    finally (callback) {
        return this.then(value => {
            return MyPromise.resolve(callback()).then(() => value)
        }, reason => {
            return MyPromise.resolve(callback()).then(() => {throw reason})
        })
    }
}

function resolvePromise (promise, result, resolve, reject) {
    if (promise === result) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if (result instanceof MyPromise) {
        // 链式调用
        result.then(resolve, reject)
    } else {
        resolve(result)
    }
}

module.exports = MyPromise